
/** path.d - The operations of path

  Written in the D programming language 1.0

Authors:	Wei Li <oldrev@gmail.com> (D)
Copyright:	Copyright (c) 2007 by Wei Li.
License:	BSD
*/

//------------------------------------------------------------------------------
// 
// System.IO.Path.cs 
//
// Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
// Copyright (C) 2002 Ximian, Inc. (http://www.ximian.com)
// Copyright (C) 2003 Ben Maurer
// 
// Author:         Jim Richardson, develop@wtfo-guru.com
//                 Dan Lewis (dihlewis@yahoo.co.uk)
//                 Gonzalo Paniagua Javier (gonzalo@ximian.com)
//                 Ben Maurer (bmaurer@users.sourceforge.net)
//                 Sebastien Pouliot  <sebastien@ximian.com>
// Created:        Saturday, August 11, 2001 
//
//------------------------------------------------------------------------------

//
// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

module dotmars.io.path;

import dotmars.base.string;
import dotmars.base.stdexcept;
import dotmars.text.utf;
import dotmars.base.algorithms;
import dotmars.base.collection.array;
import dotmars.base.random;


version(Windows)
{
	import dotmars.platform.windows.core;
}


private const char[36] FileNameChars = "0123456789abcdefghijklmnopqrstuvwxyz";


static final class Path
{

public:

version(Windows) 
{
    const char DirectorySeparator = '\\';
    const char AltDirectorySeparator = '/';
    const char PathSeparator = ';';
	const char VolumeSeparator = '\\'; 

	const dchar[41] InvalidFileNameChars = [ 
		'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
		'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', 
		'\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', 
		'\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/' ];

	const dchar[36] InvalidPathChars = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
					'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', 
					'\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', 
					'\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C' ];
}//version(Windows)
else version(Posix) 
{

    const char DirectorySeparator = '/';
    const char AltDirectorySeparator = '\\';
    const char PathSeparator = ':';
	const char VolumeSeparator = '/';
	const dchar[2] InvalidFileNameChars = [ '\x00', '/' ];
	const dchar[1] InvalidPathChars = ['\x00' ];

}//version(Posix)


	private const bool DirectoryEqualsVolume = (DirectorySeparator == VolumeSeparator);
	private const dchar[3] PathSeparators = [ DirectorySeparator,  
		AltDirectorySeparator, VolumeSeparator];

	public static string changeExtension(string path, string extension) // 	更改路径字符串的扩展名。
	in
	{
		assert(path !is null);
		assert(extension !is null);
	}
	body
	{
		if (path.indexOfAny (InvalidPathChars) >= 0)
			throw new ArgumentException ("Illegal character(s) in path");

		size_t iExt = findExtension (path);

		if (extension == null)
			return iExt < 0 ? path : path.substring (0, iExt);
		else if (extension.empty())
			return iExt < 0 ? path ~ '.' : path.substring (0, iExt + 1);

		else if (path.length != 0) {
			if (extension.length > 0 && extension [0] != '.')
				extension = "." ~ extension;
		} else
			extension.length = 0;
		
		if (iExt < 0) 
		{
			return path ~ extension;
		} else if (iExt > 0) {
			string temp = path.substring (0, iExt);
			return temp ~ extension;
		}

		return extension;
	}


	public static string combine(string path1, string path2)
	in
	{
		assert(path1 !is null);
		assert(path2 !is null);
	}
	body
	{
		if (path1.empty()) return path2;
		if (path2.empty()) return path1;

		if (path1.indexOfAny (InvalidPathChars) >= 0)
			throw new ArgumentException ("Illegal characters in path1");

		if (path2.indexOfAny (InvalidPathChars) >= 0)
			throw new ArgumentException ("Illegal characters in path2");

		if (isPathRooted(path2)) return path2;
		
		char p1end = path1 [path1.length - 1];
		if (p1end != DirectorySeparator && p1end != AltDirectorySeparator && p1end != VolumeSeparator)
			return path1 ~ DirectorySeparator ~ path2;

		return path1 ~ path2;

	}

	public static bool isPathRooted(string path)
	{
		if ((path is null) || path.empty()) return false;

			if (path.indexOfAny (InvalidPathChars) >= 0)
				throw new ArgumentException ("Illegal characters in path");

			char c = path [0];
			return (c == DirectorySeparator	||
				c == AltDirectorySeparator 	||
				(!DirectoryEqualsVolume && path.length > 1 && path [1] == VolumeSeparator));
	}


	public static string getDirectoryName(string path)
	in {
		assert(path !is null);
		assert(!path.empty());
	}
    out (result) {
		assert(result.length <= path.length);
    }
    body {
		size_t i;

		for (i = path.length; i > 0; i--)
		{
			version(Windows) {
				if (path[i - 1] == VolumeSeparator) break;
			}

			if (path[i - 1] == DirectorySeparator)
			{   
				i--;
				break;
			}
		}
		return path[0 .. i];
    }


	public static string getExtension(string path)
	{
		uint i;

		i = path.length;
		while (i > 0)
		{
			if (path[i - 1] == '.')
				return path[i .. path.length];
			i--;
			if (path[i] == VolumeSeparator || path[i] == DirectorySeparator)
				break;
		}
		return null;
	}

	public static string getFileName(string path)
    out (result)
    {
		assert(result.length <= path.length);
    }
    body
    {
		size_t i;
		for (i = path.length; i > 0; i--)
		{
			if (path[i - 1] == VolumeSeparator || path[i - 1] == DirectorySeparator)
				break;
		}
		return path[i .. path.length];
    }

	public static bool hasExtension(string path)
	{
		return findExtension(path) != path.length;
	}

	public static string getFileNameWithoutExtension(string path)
	in {
		assert(path !is null);
	}
	body {
		string name = getFileName(path);
		size_t iext = findExtension(name);
		if(iext != name.length)
			return name[0 .. iext + 1];
		return name;
	}


	public static string getPathRoot(string path)
	{

version(Windows){
		if(path.length < 2)return null;
		if(path[1] == VolumeSeparator)
			return path[0 .. 3];	
}//(windows)

version(Posix){
		if(path.length < 1)return null;
		if(path[0] == DirectorySeparator)
			return path[0 .. 1];
}//Posix

		return null;
	}

	// public static string getFullPath(string path)


// It is not too userful
/*   
	public static string getTempFileName()
	{
		auto rnd = new Random();
		string name = new char[8];

		foreach(c; name)
		{
			c = rnd.next(FileNameChars.length);
		}

		return name;
	}
*/


	public static string getTempPath()
	{
		version(Posix)
		{
			return "/tmp";
		}
		else version(Windows)
		{
			wchar[MAX_PATH] pathw;
			size_t len = GetTempPathW(pathw.length, pathw.ptr);
			return .toUtf8(pathw[0..len]);
		}
	}

	private static size_t findExtension (string path)
	in
	{
		assert(path !is null);
	}
	body
	{
		if (path != null)
		{
			size_t iLastDot = path.lastIndexOf ('.');
			size_t iLastSep = path.lastIndexOfAny ( PathSeparators );

			if (iLastDot > iLastSep) return iLastDot;
		}
		return path.length;
	}
}


